package com.martix.util;

/**
 * 64bit:<br/>
 * 1位 符号位 <br/>
 * serviceId: 服务id，启动的服务id, 并非其他雪花算法的物理机id  8bit 2^8=256 个服务 <br/>
 * sequenceBits: 序列号id, 每一毫秒内所产生的不同id数  10bit 2^10=1024 每毫秒 <br/>
 * clockBits: 时钟回拨位, 2bit 最多允许时钟回拨 2^2=4 次回拨 <br/>
 * timestampBits 43bit 2^43=278.92 为278.92年 <br/>
 * 兼容jdk1.8 <br/>
 *
 * @author liuweizhong
 * @since 2025-09-08 14:20
 */
public class SnowflakeIdUtil {
    /**
     * 机器id所占的位数
     */
    private static final long SERVICE_BITS = 8;
    /**
     * 序列号位数
     */
    private static final long SEQUENCE_BITS = 10;
    /**
     * 时钟回拨位数
     */
    private static final long CLOCK_BITS = 2;
    /**
     * 工作机器ID 默认0
     */
    private long serviceId = 0L;
    /**
     * 时间起始标记点，一旦确定不能变动（2025-09-01 00:00:00）。
     */
    private static final long startTimestamp = 1756656000000L;


    /**
     * 时间戳需要左移的位数
     */
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + SERVICE_BITS + CLOCK_BITS;
    /**
     * 时钟回拨位需要左移的位数
     */
    private static final long CLOCK_BITS_SHIFT = SEQUENCE_BITS + SERVICE_BITS;
    /**
     * 机器id向左移位数
     */
    private static final long SERVICE_BIT_SHIFT = SEQUENCE_BITS;
    /**
     * 最大时钟回拨值
     */
    private static final long MAX_CLOCK = (long) Math.pow(2, CLOCK_BITS);
    /**
     * 最大序列号值
     */
    private static final long MAX_SEQUENCE = (long) Math.pow(2, SEQUENCE_BITS);
    /**
     * 最大机器id值
     */
    private static final long MAX_SERVICE_ID = (long) Math.pow(2, SERVICE_BITS);

    /**
     * 时钟回拨值
     */
    private long clockId = 0L;

    /**
     * 上次生产 ID 时间戳。
     */
    private long lastTimeMillis = -1L;

    /**
     * 同一毫秒内的最新序号，最大值可为（2^12 - 1 = 4095）。
     */
    private long sequence;

    /**
     * 发生时钟回拨时，上一次记录的时间戳
     */
    private long clockLastTimeMillis = -1L;

    /**
     * 时钟回拨错误提示信息
     */
    private static final String CLOCK_ERROR_MSG = "Clock moved backwards";
    /**
     * 超量错误提示信息
     */
    private static final String SEQUENCE_ERROR_MSG = "IdExcess";
    /**
     * 服务id错误提示信息
     */
    private static final String SERVICE_ERROR_MSG = "ServiceIdExcess";


    private static final class InstanceHolder {
        private static final SnowflakeIdUtil INSTANCE = new SnowflakeIdUtil();
    }

    /**
     * 获取单例实例对象
     */
    public static SnowflakeIdUtil getInstance() {
        return InstanceHolder.INSTANCE;
    }

    public long generateId(long serviceId) {
        if (serviceId > MAX_SERVICE_ID || serviceId < 0) {
            throw new RuntimeException(SERVICE_ERROR_MSG);
        }
        this.serviceId = serviceId;
        return nextId();
    }

    private SnowflakeIdUtil() {
    }

    public long generateId() {
        return nextId();
    }

    public String generateIdStr(long serviceId) {
        if (serviceId > MAX_SERVICE_ID || serviceId < 0) {
            throw new RuntimeException(SERVICE_ERROR_MSG);
        }
        this.serviceId = serviceId;
        return String.valueOf(nextId());
    }

    public String generateIdStr() {
        return String.valueOf(nextId());
    }

    public synchronized long nextId() {
        long currentTimeMillis = System.currentTimeMillis();
        // 时钟回拨恢复
        if (clockId > 0L && currentTimeMillis > clockLastTimeMillis) {
            clockId = 0L;
        }
        // 当前时间小于上一次生成 ID 使用的时间，可能出现服务器时钟回拨问题。
        if (currentTimeMillis < lastTimeMillis) {
            // 将发生时钟回拨时的毫秒时间戳记录下来
            clockLastTimeMillis = lastTimeMillis;
            if (clockId < MAX_CLOCK) {
                ++clockId;
            } else {
                throw new RuntimeException(CLOCK_ERROR_MSG);
            }
        }

        if (currentTimeMillis == lastTimeMillis) {
            // 相同毫秒内，序列号自增
            ++sequence;
            if (sequence > MAX_SEQUENCE) {
                // 同一毫秒的序列数已经达到最大
                if (clockId < MAX_CLOCK) {
                    ++clockId;
                } else {
                    throw new RuntimeException(SEQUENCE_ERROR_MSG);
                }
            }
        } else {
            // 不同毫秒内，序列号置为 0。
            sequence = 0L;
        }

        // 记录最后一次使用的毫秒时间戳
        lastTimeMillis = currentTimeMillis;

        // 时间戳部分 | 时钟回拨部分 | 机器标识部分 | 序列号部分
        return ((currentTimeMillis - startTimestamp) << TIMESTAMP_SHIFT)
                | (clockId << CLOCK_BITS_SHIFT)
                | (serviceId << SERVICE_BIT_SHIFT)
                | sequence;
    }


}
